# -*- shell-script -*-

# 00minimal - Default hardware database scanning routines and variables.

# This file is part of the Linux lsvpd package.

# (C) Copyright IBM Corp. 2002, 2003, 2004, 2005

# Maintained by  Martin Schwenke <martins@au.ibm.com>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
# $Id: 00minimal,v 1.1 2006/04/11 18:38:28 emunson Exp $

# Default and common functions.

# This module is always loaded.
true || return 0

######################################################################

db_initialise_functions="db_initialise_basic db_initialise_bus"

db_initialise ()
{
    for f in $db_initialise_functions ; do
	$f
    done
}

db_initialise_bus ()
{
    ensure_directory "$db_bus_dir"
}

db_initialise_basic ()
{
    ensure_directory "$db"

    # FIXME: This is a hack.  Could it be done better elsewhere?
    ensure_directory "${db}/../lock"

    db_set_subdir_vars_hook

    db_initialise_misc
}

db_initialise_misc ()
{
    ensure_directory "$db_misc_dir"

    db_initialise_misc_uname
    db_initialise_misc_proc
}

# It might be possible to reparse the output of "uname -a", but this
# is likely to be much more systematic.
db_initialise_misc_uname ()
{
    ensure_directory "$db_uname_dir"

    for i in "kernel-name" "kernel-release" "kernel-version" \
	"machine" "nodename" ; do
      # Hey, there're 2 completely different versions of uname!
      local flag
      case "$i" in
	  kernel-name)    flag="-s"     ;;
	  kernel-release) flag="-r"     ;;
	  kernel-version) flag="-v"     ;;
	  *)              flag="--${i}" ;;
      esac
      uname "$flag" > "${db_uname_dir}/${i}"
    done
}

db_initialise_misc_proc ()
{
    ensure_directory "$db_proc_dir"

    for i in "partitions" "cpuinfo" ; do
	[ -f "/proc/${i}" ] && cat "/proc/${i}" >"${db_proc_dir}/${i}"
    done
}

######################################################################

list_buses ()
{
    :
}

add_bus ()
{
    :
}

######################################################################

node_add ()
{
    local meta_type="$1"
    local bus_type="$2"
    local bus_addr="$3"

    local parent_subtype
    get_parent_subtype "$meta_type" "$bus_type" "$bus_addr"
    [ -n "$parent_subtype" ] || return 1  ### !!!

    local tmp_dir="${db_tmp_dir}/${bus_addr}"
    ensure_directory "$tmp_dir"

    if retrieve_vpd "$meta_type" "$parent_subtype" \
	"${bus_type}/${bus_addr}" "$tmp_dir" ; then

	local subtype
	local f="${tmp_dir}/lsvpd,subtype"
	[ -r "$f" ] && read subtype <"$f"
	if [ -n "$subtype" ] ; then
	    local node source_node yl

	    if node_add_hook "$meta_type" "$parent_subtype" \
		"${bus_type}/${bus_addr}" "$subtype" ; then

		move_directory "$tmp_dir" "$node"

		f="${node}/lsvpd,bus-info"
		[ ! -f "$f" ] && echo -n "${bus_type}/${bus_addr}" >"$f"

		local fc vpd_subdirs vpd_dir
		do_fc_hook "$source_node"
		vpd_subdirs_list_hook "$node"
		for vpd_dir in $vpd_subdirs ; do
		    vpd_field_ensure "$vpd_dir" "YL" "$yl"
		    vpd_field_override "$vpd_dir" "FC" "$fc"
		done

		# Logicals here are channels.  For devices, it might
		# be worth dig this for LUNs.
		process_logicals "$meta_type" "$bus_type" "$bus_addr" \
		    "$source_node" "$node" "$fc"
	    fi
	fi
    fi

    # Remove temporary directory if it still exists.  Be careful!
    if [ -d "$tmp_dir" -a -n "$db" -a "${tmp_dir#${db}}" != "$tmp_dir" ] ; then
	rm -rf "$tmp_dir"
    fi
}

node_add_hook ()
{
    # Sets: node, source_node, yl

    local meta_type="$1"
    local node_type="$2"
    local bus_info="$3"
    local subtype="$4"

    local bus_type="${bus_info%/*}"
    local bus_addr="${bus_info#*/}"

    get_node "$meta_type" "$node_type" "$bus_info" "$subtype"
    [ -n "$node" ] || return 1  ### !!!

    get_source_node "$meta_type" "$node_type" "$bus_info" "$subtype"
    [ -n "$source_node" ] || return 1  ### !!!

    local parent_node
    get_parent_node "$meta_type" "$bus_type" "$bus_addr"
    [ -n "$parent_node" ] || return 1  ### !!!

    backlink_parent_node "$node" "$parent_node"

    get_yl "$meta_type" "$bus_type" "$bus_addr" "$source_node" "$node"

    node_add_extra_hook "$meta_type" "$node_type" "$bus_info" "$subtype"
    return 0
}

node_add_name ()
{
    local meta_type="$1"
    local bus_type="$2"
    local bus_addr="$3"

    local subtype
    retrieve_subtype "$bus_type" "$bus_addr"
    [ -n "$subtype" ] || return  ### !!!

    local names
    get_names "$meta_type" "$bus_type" "$bus_addr"

    local node
    retrieve_node "${bus_type}/${bus_addr}"
    [ -n "$node" ] || return  ### !!!

    crosslink "$meta_type" "$subtype" "$node" "$names"

    local ax
    names_to_ax "$meta_type" "$subtype" "$names"

    vpd_node_override_value "$node" "AX" "$ax"

    do_logicals_ax "$meta_type" "$subtype" "$node" "$names"
}

make_multiplexed get_names
get_names_DEFAULT ()
{
    # Sets: names
    names=""

    local meta_type="$1"
    local bus_type="$2"
    local bus_addr="$3"
}

make_multiplexed get_node
get_node_DEFAULT ()
{
    # Sets: node
    node=""

    local meta_type="$1"
    local node_type="$2"
    local bus_info="$3"
    local subtype="$4"

    local bus_type="$1"
    local bus_addr="$2"

    retrieve_node "$bus_info"
    [ -n "$node" ] && return 0  ### !!!

    local source_node
    get_source_node "$meta_type" "$node_type" "$bus_info" "$subtype"
    [ -n "$source_node" ] || return 1  ### !!!

    node="${db_bus_root}/${source_node#${source_root}/}"
    bus_alias_set "$bus_info" "$node"
}

make_multiplexed retrieve_vpd
make_multiplexed process_logicals

make_multiplexed get_parent_subtype
get_parent_subtype_DEFAULT ()
{
    # Sets: parent_subtype

    local bus_type="$1"
    local bus_addr="$2"

    parent_subtype=""
}


make_multiplexed get_parent_node
get_parent_node_DEFAULT ()
{
    # Sets: parent_node

    local bus_type="$1"
    local bus_addr="$2"

    parent_node="."
}

make_multiplexed get_yl
get_yl_DEFAULT ()
{
    # Sets: yl
    yl=""

    local meta_type="$1"
    local bus_type="$2"
    local bus_addr="$3"
    local source_node="$4"
    local node="$5"
}

make_multiplexed node_add_extra_hook

make_multiplexed get_source_node
get_source_node_DEFAULT ()
{
    # Sets: source_node
    source_node=""

    local meta_type="$1"
    local node_type="$2"
    local bus_info="$3"
    local subtype="$4"
}

make_multiplexed names_to_ax
names_to_ax_DEFAULT ()
{
    # Sets: ax
    ax=""

    local subtype="$1"
    local names="$2"
}

make_multiplexed do_logicals_ax

######################################################################

do_fc_hook ()
{
    local dir="$1"

    # By default, VPD is marked global.
    fc='********'
}

######################################################################

crosslink ()
{
    local meta_type="$1"
    local subtype="$2"
    local node="$3"
    local names="$4"

    local os_subdir
    get_os_subdir "$meta_type" "$subtype"
    [ -n "$os_subdir" ] || return  ### !!!

    # It is very important that $os_subdir and $names are used here
    # instead of $subtype and $ax.  This makes it possible to find
    # $node later on without knowing the subtype.  All that is needed
    # is a general subtype that shares the same $os_subdir and
    # $os_prefix.  For example, when searching for the parent
    # (adapter) node for a SATA device, it is enough to know the
    # (general) subtype "scsi", since this is associated with the same
    # $os_subdir and $os_prefix as the true subtype "sata".

    local os_parent_node="${db_os_dir}/${meta_type}/${os_subdir}"

    # Create links to bus node.
    local n
    for n in $names ;do
	local os_node="${os_parent_node}/${n}"
	local l="${os_node}/bus-node"

	[ -L "$l" ] && rm -f "$l"

	ensure_directory "$os_node"
	local bus_path="${node#${db_bus_dir}/}"
	set_dot_dots_hook "$os_node" "$db" && \
	    ln -s "${dot_dots}/${db_bus_subdir}/${bus_path}" "$l"
    done

    # Save names.
    [ -n "$names" ] && \
	echo "$names" >"${node}/lsvpd,names"
}

make_multiplexed get_os_subdir
get_os_subdir_DEFAULT ()
{
    # Sets: os_subdir

    local meta_type="$1"
    local subtype="$2"

    os_subdir="$subtype"
}

backlink_parent_node ()
{
    local node="$1"
    local parent_node="$2"

    [ "$parent_node" = "." ] && return  ### !!!

    local l="${node}/lsvpd,parent-node"
    if set_dot_dots_hook "$node" "$parent_node" ; then
	rm -f "$l"
	ensure_directory "$node"
	ln -s "$dot_dots" "$l"
    fi
}

set_dot_dots_hook ()
{
    local d="$1"
    local t="$2" # Must be a prefix of $d

    dot_dots=""

    if [ "${d}" = "${d#${t}/}" ] ; then
	debug "set_dot_dots_hook: ${t} is not a prefix of ${d}"
	return 1
    fi

    while [ -n "$d" -a "$d" != "$t" ] ; do
	dot_dots="${dot_dots}/.."
	local d="${d%/*}" # dirname
    done

    dot_dots="${dot_dots#/}"
}

######################################################################

retrieve_node ()
{
    # Sets: node
    node=""

    local bus_info="$1"

    local bus_alias
    bus_alias_get "$bus_info"
    node="$bus_alias"
}

retrieve_subtype ()
{
    # Sets: subtype
    subtype=""

    local bus_type="$1"
    local bus_addr="$2"

    local node
    retrieve_node "${bus_type}/${bus_addr}"
    retrieve_subtype_basic "$node"
}

retrieve_subtype_basic ()
{
    # Sets: subtype
    subtype=""

    local node="$1"

    [ -n "$node" ] || return 1 ### !!!

    local f="${node}/lsvpd,subtype"
    [ -r "$f" ] && read subtype <"$f"
}

######################################################################

bus_alias_set ()
{
    local bus_info="$1"
    local target="$2"
    local alias_type="${3:-bus-alias}" # optional

    local dir="${db_bus_dir}/${bus_info}"
    local key="${dir}/${alias_type}"

    if [ ! -f "$key" ] ; then
	ensure_directory "$dir"
	echo -n "$target" >"$key"
	return 0
    else
	return 1
    fi
}

bus_alias_clear ()
{
    local bus_info="$1"
    local alias_type="${2:-bus-alias}" # optional
    
    local dir="${db_bus_dir}/${bus_info}"
    local key="${dir}/${alias_type}"

    rm -f "$key"
    rmdir "$dir" >/dev/null 2>&1
}

bus_alias_get ()
{
    # Sets: bus_alias
    bus_alias=""

    local bus_info="$1"
    local alias_type="${2:-bus-alias}" # optional
    
    local key="${db_bus_dir}/${bus_info}/${alias_type}"
    [ -r "$key" ] && \
	read bus_alias <"$key"
}

######################################################################

set_sequence_number_hook ()
{
    local prefix="$1"
    local min="${2:-0}"

    ensure_directory "$db_state_dir"
    local f="${db_state_dir}/${prefix}"

    sequence_number=""

    if lock_file "$f" ; then
	if [ -f "$f" ] ; then
	    read sequence_number <"$f"
	else
	    sequence_number="$min"
	fi
	echo $(($sequence_number + 1)) >"$f"
	unlock_file "$f"
    fi
}
